package nl.sweet.testsweet.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.util.LinkedList;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

import nl.sweet.testsweet.TestSweet;
import nl.sweet.testsweet.TestSweetListener;
import nl.sweet.testsweet.TestSweetTestCase;
import nl.sweet.testsweet.TestSweetXMLHandler;

/**
 * The main application frame for TestSweet.
 * @author wessels
 */
public class TestSweetFrame extends JFrame
{
	/** For serialization. */
	private static final long serialVersionUID = 1L;

	/** The TestSweet application. */
	private TestSweetApplication _app;

	/** The label with the status text. */
	private JLabel _statusText;

	/** The progress bar of the application. */
	private JProgressBar _progressBar;

	/** The results panel for the results of the tests. */
	private JEditorPane _resultsPanel;

	/** The buffer with the string for the results panel. */
	private StringBuffer _resultsStringBuffer;

	/** The button to save the XML file. */
	private JButton _saveToXMLButton;

	/** The button to save the HTML file. */
	private JButton _saveToHTMLButton;

	private JTabbedPane _tabPane;

	private JList _testCasesList;

	/** The html prefix. */
	private static final String HTML_PREFIX = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">"
			+ "<html><head><style>" + "body{font-family:verdana;}"
			+ "table{font-size:10pt;}th{text-align:left;}" + ".pass{color:green;}.fail{color:red;}"
			+ "</style></head><body><table width=\"100%\">"
			+ "<tr><th>Testcase</th><th width=\"60\">Passed?</th></tr>";

	/** The html closing stuff. */
	private static final String HTML_ENDING = "</body></html>";

	/**
	 * Constructor.
	 * @param app The test sweet application.
	 */
	public TestSweetFrame(TestSweetApplication app)
	{
		super("TestSweet " + TestSweet.APPLICATON_VERSION);

		setIconImage(new ImageIcon(TestSweetFrame.class.getResource("candy_icon.gif")).getImage());

		_app = app;
		_resultsStringBuffer = new StringBuffer();

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		getContentPane().setLayout(new BorderLayout());

		// Create a panel for the status and progress bar.
		JPanel statusAndProgressPane = new JPanel(new BorderLayout());

		// Add a label with the status to the GUI.
		_statusText = new JLabel("Ready.");
		statusAndProgressPane.add(_statusText, BorderLayout.NORTH);

		// Add a progress bar to the GUI.
		_progressBar = new JProgressBar(0, 0);
		statusAndProgressPane.add(_progressBar, BorderLayout.CENTER);
		getContentPane().add(statusAndProgressPane, BorderLayout.NORTH);

		_tabPane = new JTabbedPane();

		// Add a list with testcases to perform.
		JPanel testCases = new JPanel(new BorderLayout());
		testCases.add(new JLabel("Select the tests to perform:"), BorderLayout.NORTH);
		_testCasesList = new JList(_app.getTestCases().toArray());
		testCases.add(_testCasesList, BorderLayout.CENTER);
		_tabPane.addTab("Testcases", new JScrollPane(testCases));

		// Add a editorpane for the html generation.
		_resultsPanel = new JEditorPane("text/html", HTML_PREFIX + "No test results." + HTML_ENDING);
		_resultsPanel.setEditable(false);
		_resultsPanel.setPreferredSize(new Dimension(TestSweetApplication.RESULTS_WINDOW_WIDTH,
				TestSweetApplication.RESULTS_WINDOW_HEIGHT));
		_tabPane.addTab("Summary", new JScrollPane(_resultsPanel));

		getContentPane().add(_tabPane, BorderLayout.CENTER);

		// Add a save button to the application.
		JPanel buttons = new JPanel();
		JButton startTesting = new JButton(new PerformTestAction());
		_saveToXMLButton = new JButton(new SaveToXMLAction(_app));
		_saveToHTMLButton = new JButton(new SaveToHTMLAction(_app));
		buttons.add(startTesting);
		buttons.add(_saveToXMLButton);
		buttons.add(_saveToHTMLButton);
		getContentPane().add(buttons, BorderLayout.SOUTH);
		_saveToXMLButton.setEnabled(false);
		_saveToHTMLButton.setEnabled(false);
		pack();

		// Position the frame on the window.
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Dimension screenSize = toolkit.getScreenSize();
		int x = (screenSize.width - getWidth()) / 2;
		int y = (screenSize.height - getHeight()) / 2;
		setLocation(x, y);

		// Show the frame.
		setVisible(true);
		toFront();
	}

	/**
	 * Set the status text of the status bar.
	 */
	public void setDone()
	{
		setStatusText("Done.");
		_progressBar.setValue(0);
		_saveToXMLButton.setEnabled(true);
		_saveToHTMLButton.setEnabled(true);
		TestSweetXMLHandler xmlHandler = new TestSweetXMLHandler(_app.getTestSweetResults(), _app
				.getTotalTime());
		String generatedHTML = xmlHandler.generateHTMLFromXML();
		// Delete the meta tag so it will be rendered in the JEditorPane.
		generatedHTML = generatedHTML.replaceFirst("<META.*>", "");
		generatedHTML = generatedHTML.replaceFirst("font-size.*pt;", "font-size: 11pt;");
		JEditorPane editorPane = new JEditorPane("text/html", generatedHTML);
		editorPane.setEditable(false);
		_tabPane.addTab("Results", new JScrollPane(editorPane));
		_tabPane.setSelectedIndex(_tabPane.indexOfTab("Results"));
	}

	/**
	 * Set the total of tests which will be performed.
	 * @param amountTests The total amount of test performed.
	 */
	public void setTotalTests(int amountTests)
	{
		_progressBar.setMaximum(amountTests);
	}

	/**
	 * Update the progress bar.
	 * @param currentTestcase The test case it is currently running.
	 */
	public void setProgressBarValue(int currentTestcase)
	{
		_progressBar.setValue(currentTestcase);
	}

	/**
	 * Set the status text.
	 * @param statusText The status text.
	 */
	public void setStatusText(String statusText)
	{
		_statusText.setText(statusText);
	}

	/**
	 * Set the results
	 * @param resultsContent
	 */
	public void setResultsContent(String resultsContent)
	{
		_resultsPanel.setText(resultsContent);
	}

	/**
	 * Add result content to the results window.
	 * @param testName The name of the test.
	 * @param succes Boolean wether the test was succesfull.
	 */
	public void addResultsContent(String testName, boolean succes)
	{
		String resultName = (succes) ? "PASSED" : "FAILED";
		String resultClass = (succes) ? "pass" : "fail";
		_resultsStringBuffer.append("<tr><td>"
				+ testName + "</td><td class=\"" + resultClass + "\">" + resultName + "</td></tr>");
		updateResultsPanel();
	}

	/**
	 * Update the results panel.
	 */
	public void updateResultsPanel()
	{
		_resultsPanel.setText(HTML_PREFIX + _resultsStringBuffer.toString() + HTML_ENDING);

		// Scroll the text as it gets more and more.
		SwingUtilities.invokeLater(new Runnable()
		{
			public void run()
			{
				_resultsPanel.scrollRectToVisible(new Rectangle(0, _resultsPanel.getHeight() - 2,
						1, 1));
			}
		});
	}

	private class PerformTestAction extends AbstractAction
	{
		/** Voor serializatie. */
		private static final long serialVersionUID = 1L;

		/**
		 * Constructor.
		 */
		public PerformTestAction()
		{
			super("Start testing");
		}

		/**
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		@SuppressWarnings("unchecked")
		public void actionPerformed(ActionEvent e)
		{
			// Remove the results if there are any.
			if (_tabPane.getTabCount() > 2)
			{
				_resultsStringBuffer = new StringBuffer();
				updateResultsPanel();
				_tabPane.remove(2);
			}

			final LinkedList<Class< ? extends TestSweetTestCase>> testCasesList = new LinkedList<Class< ? extends TestSweetTestCase>>();
			for (int i = 0; i < _testCasesList.getSelectedIndices().length; ++i)
			{
				Class< ? extends TestSweetTestCase> testClass = (Class< ? extends TestSweetTestCase>) _testCasesList
						.getModel().getElementAt(_testCasesList.getSelectedIndices()[i]);
				testCasesList.add(testClass);
			}
			_tabPane.setSelectedIndex(1);
			new TestSweetThread(testCasesList, _app).start();
		}
	}

	private class TestSweetThread extends Thread
	{
		private LinkedList<Class< ? extends TestSweetTestCase>> _testCasesList;

		private TestSweetListener _testListener;

		public TestSweetThread(LinkedList<Class< ? extends TestSweetTestCase>> testCasesList,
				TestSweetListener testListener)
		{
			_testCasesList = testCasesList;
			_testListener = testListener;
		}

		public void run()
		{
			new TestSweet(_testCasesList, _testListener);
		}
	}
}
